package com.norra.base.api


import com.google.gson.JsonParseException
import com.norra.base.api.entity.BaseResponse
import retrofit2.*
import java.io.IOException
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.concurrent.Executor

class MyCallAdapterFactory : CallAdapter.Factory() {
    override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {
        if (CallAdapter.Factory.getRawType(returnType) != MyCall::class.java) {
            return null
        }
        if (returnType !is ParameterizedType) {
            throw IllegalStateException("MyCall must have generic type (e.g., MyCall<ResponseBody>)")
        }
        val responseType = CallAdapter.Factory.getParameterUpperBound(0, returnType)
        val callbackExecutor = retrofit.callbackExecutor()

        return ErrorHandlingCallAdapter<Any>(responseType, callbackExecutor!!)
    }

    private class ErrorHandlingCallAdapter<R> internal constructor(private val responseType: Type, private val callbackExecutor: Executor) : CallAdapter<R, MyCall<R>> {

        override fun responseType(): Type {
            return responseType
        }

        override fun adapt(call: Call<R>): MyCall<R> {
            return MyCallAdapter(call, callbackExecutor)
        }
    }

    internal class MyCallAdapter<T>(private val call: Call<T>, private val callbackExecutor: Executor) : MyCall<T> {

        override fun cancel() {
            call.cancel()
        }

        @Throws(IOException::class)
        override fun execute(): T? {
            return call.execute().body()
        }

        override fun enqueue(callback: ApiCallback<T>?) {
            call.enqueue(object : Callback<T> {
                override fun onResponse(call: Call<T>, response: Response<T>) {
                    val code = response.code()
                    if (code in 200..299) {
                        val body = response.body();
                        if (body != null && body is BaseResponse) {
                            val baseResponse = (body as BaseResponse?)!!
                            if (baseResponse.isSuccess) {
                                postSuccess(callback, body)
                            } else {
                                if (baseResponse.isTokenExpired) {
                                    //TODO
                                    BaseApp.get().onUserTokenExpired()
                                }
                                postError(callback, HError.create(HError.ERROR_BUSSINESS, "${baseResponse.code}", baseResponse.msg))
                            }
                        } else {
                            postError(callback, HError.create(HError.ERROR_UNKNOWN, "Response is null"))
                        }
                    } else {
                        postError(callback, HError.create(HError.ERROR_SERVER, "server error"))
                    }
                }

                override fun onFailure(call: Call<T>, t: Throwable) {
                    val error = when (t) {
                        is IOException -> HError.create(HError.ERROR_NETWORK, "网络异常")
                        is JsonParseException -> HError.create(HError.ERROR_JSON, t.message)
                        else -> HError.create(HError.ERROR_UNKNOWN, t.message)
                    }
                    postError(callback, error)
                }
            })
        }

        private fun postSuccess(callback: ApiCallback<T>?, response: T) {
            if (callback == null)
                return
            callbackExecutor.execute { callback.onSuccess(response) }
        }

        private fun postError(callback: ApiCallback<T>?, error: HError) {
            if (callback == null)
                return
            callbackExecutor.execute { callback.onError(error) }
        }

        override fun clone(): MyCall<T> {
            return MyCallAdapter(call.clone(), callbackExecutor)
        }
    }
}
